/**
 * Copyright (C) 2012 Droidhang Inc.
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Droidhang Inc.
 * The contents of this file may not be disclosed to third parties, copied or
 * duplicated in any form, in whole or in part, without the prior written
 * permission of Droidhang Inc.
 *
 * @author yangsheng.cheng
 *
 */

#include <cmath>
#include "gamescene.h"
#include "resourcemanager.h"
#include "gamemanager.h"
#include "gamedatamanager.h"
#include "messagequeue.h"
#include "army.h"
#include "strutil.h"
#include "armiesscene.h"
#include "log.h"
#include "CCSkeletalSprite.h"
#include "unitfactory.h"
#include "frameanimation.h"
#include "bullet.h"
#include "defeatlayer.h"
#include "victorylayer.h"
#include "campaignscene.h"
#include "battlescene.h"

static const int LEFT_LINES_X_POS[] = {
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 14 + SQUAD_SPACE * 4,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 13 + SQUAD_SPACE * 4,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 12 + SQUAD_SPACE * 4,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 11 + SQUAD_SPACE * 3,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 10 + SQUAD_SPACE * 3,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 9 + SQUAD_SPACE * 3,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 8 + SQUAD_SPACE * 2,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 7 + SQUAD_SPACE * 2,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 6 + SQUAD_SPACE * 2,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 5 + SQUAD_SPACE,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 4 + SQUAD_SPACE,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 3 + SQUAD_SPACE,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH * 2,
		FORMATION_MARGIN + GRID_WIDTH / 2 + GRID_WIDTH,
		FORMATION_MARGIN + GRID_WIDTH / 2,
};

static const int OPPONENT_START_X_POS = LEFT_LINES_X_POS[0] + FORMATION_SPACE + GRID_WIDTH;

static const int RIGHT_LINES_X_POS[] = {
		GRID_WIDTH / 2,
		GRID_WIDTH / 2 + GRID_WIDTH,
		GRID_WIDTH / 2 + GRID_WIDTH * 2,

		GRID_WIDTH / 2 + GRID_WIDTH * 3 + SQUAD_SPACE,
		GRID_WIDTH / 2 + GRID_WIDTH * 4 + SQUAD_SPACE,
		GRID_WIDTH / 2 + GRID_WIDTH * 5 + SQUAD_SPACE,

		GRID_WIDTH / 2 + GRID_WIDTH * 6 + SQUAD_SPACE * 2,
		GRID_WIDTH / 2 + GRID_WIDTH * 7 + SQUAD_SPACE * 2,
		GRID_WIDTH / 2 + GRID_WIDTH * 8 + SQUAD_SPACE * 2,

		GRID_WIDTH / 2 + GRID_WIDTH * 9 + SQUAD_SPACE * 3,
		GRID_WIDTH / 2 + GRID_WIDTH * 10 + SQUAD_SPACE * 3,
		GRID_WIDTH / 2 + GRID_WIDTH * 11 + SQUAD_SPACE * 3,

		GRID_WIDTH / 2 + GRID_WIDTH * 12 + SQUAD_SPACE * 4,
		GRID_WIDTH / 2 + GRID_WIDTH * 13 + SQUAD_SPACE * 4,
		GRID_WIDTH / 2 + GRID_WIDTH * 14 + SQUAD_SPACE * 4,
};

static const char *SCENE_BG_NAMES[] = {
		"textures/scenes/castle.jpg",
		"textures/scenes/bridge.jpg",
		"textures/scenes/cave.jpg",
		"textures/scenes/ruin.jpg",
		"textures/scenes/snow_hill.jpg",
		"textures/scenes/ruined_gondor_castle.jpg",
		"textures/scenes/aragon_forest.jpg",
};

static const float ENDLESS_BATTLE_TIME = 60.0f;

GameScene::GameScene() :
_uiLayer(nullptr)
, _bgLayer(nullptr)
, _gameLayer(nullptr)
, _btnResumeGame(nullptr)
, me(nullptr)
, opponent(nullptr)
, myArmy(nullptr)
, opponentArmy(nullptr)
, isPausedGame(false)
, _deaths(0)
, _arrivals(0)
, _idles(0)
, gameState(GameState::start)
, _bgTextureName(nullptr)
, _labelBattleState(nullptr)
, previousScene(MessageTarget::armies_scene)
, battleState(BattleState::standby) {

}

GameScene::~GameScene() {
	if (myArmy) {
		delete myArmy;
	}

	if (opponentArmy) {
		delete opponentArmy;
	}

	for (Bullet *bullet : _bullets) {
		delete bullet;
	}

	CCTextureCache::sharedTextureCache()->removeTextureForKey(_bgTextureName);
}

bool GameScene::init() {
	if (!CCScene::init()) {
		return false;
	}

	_bgLayer = CCLayer::create();

	_gameLayer = CCLayer::create();
	CCSize gameLayerSize = CCSize(GameManager::BASE_WIDTH, GameManager::BASE_HEIGHT);
	_gameLayer->setContentSize(gameLayerSize);
	_gameLayer->setAnchorPoint(ccp(0.0f, 0.0f));
	//_gameLayer->setPosition(ccp((GM->screenSize.width - gameLayerSize.width * GM->minscale) / 2.0f, (GM->screenSize.height - gameLayerSize.height * GM->minscale) / 2.0f));
	//_gameLayer->setScale(GM->minscale);

	_uiLayer = CCLayer::create();

	addChild(_bgLayer);
	addChild(_gameLayer);
	addChild(_uiLayer);

	createUI();

	GM->gameScene = this;

	scheduleUpdate();

	return true;
}

void GameScene::createUI() {
	int count = sizeof(SCENE_BG_NAMES) / sizeof(char *);
	int index = rand() % count;
	_bgTextureName = SCENE_BG_NAMES[index];
	CCSprite *spr = CCSprite::create(_bgTextureName);
	spr->setAnchorPoint(CCPointZero);

	CCSize size = spr->getContentSize();
	spr->setScaleX(GM->screenSize.width / size.width);
	spr->setScaleY(GM->screenSize.height / size.height);
	_bgLayer->addChild(spr);

	_btnResumeGame = RM->makeButton("button/paly_game.png", nullptr, nullptr, this, menu_selector(GameScene::playGame), _uiLayer);
	_btnResumeGame->setPosition(SCALE_PT(ccp((480), (530))));

	auto btn = RM->makeButton("button/blue_button_0.png", "button/blue_button_1.png", nullptr, this, menu_selector(GameScene::restartGame), _uiLayer);
	btn->setPosition(SCALE_PT(ccp(200, 530)));

	CCLabelTTF *label = CCLabelTTF::create("Restart", "", 24);
	label->setPosition(ccp(btn->getContentSize().width / 2, btn->getContentSize().height / 2));
	btn->addChild(label);

	_btnSurrender = RM->makeButton("button/surrender_0.png", "button/surrender_0.png", nullptr, this, menu_selector(GameScene::concede), _uiLayer);
	_btnSurrender->setPosition(SCALE_PT(ccp(40, 530)));

	CCSprite *sprTimerPad = MAKE_SPRITE("timer_pad.png");
	sprTimerPad->setPosition(XY_SCALE(GameManager::BASE_WIDTH / 2, 450));
	_uiLayer->addChild(sprTimerPad);

	_labelTime = CCLabelTTF::create("00:00", "", 24);
	_labelTime->setPosition(ccp(sprTimerPad->getContentSize().width / 2, sprTimerPad->getContentSize().height / 2));
	sprTimerPad->addChild(_labelTime);

	_labelBattleState = CCLabelTTF::create("00:00", "", 24);
	_labelBattleState->setPosition(XY_SCALE(GameManager::BASE_WIDTH / 2, 360));
	_uiLayer->addChild(_labelBattleState);

#if !CC_PLATFORM_WIN32
	_labelBattleState->setVisible(false);
#endif

}

void GameScene::createArmies() {
	if (!me || !opponent) {
		_DHLOG("error: not all formations specified.");
		return;
	}

	myArmy = new Army();
	myArmy->faction = ArmyFaction::left;
	opponentArmy = new Army();
	opponentArmy->faction = ArmyFaction::right;

	Unit::ID_SEED = 1;

	// my formation
	memcpy(myArmy->orders, me->orders, sizeof(me->orders));
	for (int i = 0; i < TOTAL_LINES; i++) {
		for (int j = 0; j < ROWS_PER_LINE; j++) {
			if (me->grids[i][j] == "") {
				continue;
			}

			string unitName = me->grids[i][j];
			UnitInfo info = me->getUnitInfo(unitName);
			Unit *unit = UnitFactory::createUnit(info, 0.45f);
			unit->faction = ArmyFaction::left;
			unit->setPosition(ccp(LEFT_LINES_X_POS[i], j * GRID_HEIGHT + FORMATION_MARGIN));
			unit->gridInfo.set(i * ROWS_PER_LINE + j);
			unit->targetGridInfo.set(i * ROWS_PER_LINE + j);
			unit->oldGridInfo = unit->gridInfo;
			unit->ourArmy = myArmy;
			unit->theirArmy = opponentArmy;
			unit->gameScene = this;
			_gameLayer->addChild(unit->sprite);
			_gameLayer->reorderChild(unit->sprite, static_cast<int>(GM->screenSize.height - unit->position.y));

			myArmy->soldiers[i][j] = unit;
			myArmy->updateGridsState(unit, i, j);
			myArmy->updatePositionTable(unit, i, j);
		}
	}

	myArmy->frontLinePosition = LEFT_LINES_X_POS[0];

	// opponent formation
	memcpy(opponentArmy->orders, opponent->orders, sizeof(opponent->orders));
	for (int i = 0; i < TOTAL_LINES; i++) {
		for (int j = 0; j < ROWS_PER_LINE; j++) {
			if (opponent->grids[i][j] == "") {
				continue;
			}

			string unitName = opponent->grids[i][j];
			UnitInfo info = opponent->getUnitInfo(unitName);

			Unit *unit = UnitFactory::createUnit(info, 0.45f);
			unit->faction = ArmyFaction::right;
			unit->setPosition(ccp(OPPONENT_START_X_POS + RIGHT_LINES_X_POS[i], j * GRID_HEIGHT + FORMATION_MARGIN));
			unit->gridInfo.set(i * ROWS_PER_LINE + j);
			unit->targetGridInfo.set(i * ROWS_PER_LINE + j);
			unit->oldGridInfo = unit->gridInfo;
			unit->ourArmy = opponentArmy;
			unit->theirArmy = myArmy;
			unit->sprite->setFlipX(true);
			unit->gameScene = this;
			_gameLayer->addChild(unit->sprite);
			_gameLayer->reorderChild(unit->sprite, static_cast<int>(GM->screenSize.height - unit->position.y));

			opponentArmy->soldiers[i][j] = unit;
			opponentArmy->updateGridsState(unit, i, j);
			opponentArmy->updatePositionTable(unit, i, j);
		}
	}

	opponentArmy->frontLinePosition = OPPONENT_START_X_POS + RIGHT_LINES_X_POS[0];
}

void GameScene::update(float ticks) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
//	ticks = 0.034f;
#endif

	handleMessage();

	switch (gameState) {
		case GameState::playing: {
			_totalTicks += ticks;

			_labelBattleState->setString(BATTLE_STATE_NAMES[(int) battleState]);

			updateManagedGameNodes(ticks);
			updateBattlefieldTimer();
			updateBullets(ticks);

			myArmy->update(ticks);
			opponentArmy->update(ticks);

			_deaths = myArmy->deaths + opponentArmy->deaths;
			_arrivals = myArmy->arrivals + opponentArmy->arrivals;
			_idles = myArmy->arrivals + opponentArmy->idles;

			switch (battleState) {
				case BattleState::standby: {
					handleBattleStandby(ticks);
					break;
				}
				case BattleState::march: {
					handleBattleMarch(ticks);
					break;
				}
				case BattleState::melee: {
					handleBattleMelee(ticks);
					break;
				}
				default:
					break;
			}

//			myArmy->updateFormationState();
//			opponentArmy->updateFormationState();

			if (myArmy->frontLine >= TOTAL_LINES) {
				gameState = GameState::defeat;
				if (_totalTicks < 10.0f) {
					_DHLOG("something wrong");
				}
			} else if (opponentArmy->frontLine >= TOTAL_LINES) {
				gameState = GameState::win;
			}

			break;
		}
		case GameState::defeat:
		case GameState::win:
		case GameState::draw:
			createDialogLayer();
	        gameState = GameState::end;
	        break;
		case GameState::end:
			_btnResumeGame->setEnabled(false);
	        _btnSurrender->setEnabled(false);
	        break;
		default:
			break;
	}
}

void GameScene::updateBattlefieldTimer() {
	int minutes = static_cast<int>(_totalTicks / 60);
	int seconds = static_cast<int>(_totalTicks) % 60;

	sprintf(buf, "%02d:%02d", minutes, seconds);
	_labelTime->setString(buf);
}

void GameScene::handleMessage() {
	MessageData *msg = PEEK_MSG(MessageTarget::game_scene);
	if (msg) {
		switch (msg->key) {
			case MessageFlag::resumeGame:
				break;
			case MessageFlag::pauseGame:
				break;
			default:
				break;
		}

		POP_MSG(MessageTarget::game_scene);
	}
}


void GameScene::playGame(CCObject *sender) {
	_totalTicks = 0.0f;
	_endlessBattleTicks = 0.0f;
	gameState = GameState::playing;
	transformBattleState(BattleState::standby);

	CCMenuItemSprite *button = (CCMenuItemSprite *) sender;
	button->setTarget(this, menu_selector(GameScene::pauseGame));
	((CCSprite *) button->getNormalImage())->setDisplayFrame(MAKE_SPRITEFRAME("button/resume_game.png"));
}

void GameScene::restartGame(CCObject *sender) {
	gameState = GameState::playing;
	transformBattleState(BattleState::standby);

	_btnResumeGame->setVisible(true);
	_totalTicks = 0.0f;
	_endlessBattleTicks = 0.0f;

	for (Bullet *bullet : _bullets) {
		delete bullet;
	}
	_bullets.clear();

	delete myArmy;
	delete opponentArmy;

	createArmies();

	myArmy->updateFormationState();
	opponentArmy->updateFormationState();
}

void GameScene::pauseGame(CCObject *sender) {
	CCMenuItemSprite *button = (CCMenuItemSprite *) sender;
	if (gameState == GameState::playing) {
		((CCSprite *) button->getNormalImage())->setDisplayFrame(MAKE_SPRITEFRAME("button/paly_game.png"));
		gameState = GameState::paused;
	} else {
		((CCSprite *) button->getNormalImage())->setDisplayFrame(MAKE_SPRITEFRAME("button/resume_game.png"));
		gameState = GameState::playing;
	}
}

void GameScene::concede(CCObject *sender) {
	CCScene* scene = nullptr;
	switch(previousScene) {
		case MessageTarget::campaign_scene:
			scene = CampaignScene::create();
	        break;
		case MessageTarget::online_battle_scene:
			scene = BattleScene::create();
	        break;
		default:
			scene = ArmiesScene::create();
	        break;
	}

	CCDirector::sharedDirector()->replaceScene(scene);
}


float GameScene::getContactPosition() {
	float left = myArmy->frontLinePosition;
	float right = opponentArmy->frontLinePosition;

	float mid = left + (right - left) / 2.0f;
	_DHLOG("contact position, left:%0.0f, mid:%0.0f, right:%0.0f", left, mid, right);
	return mid;
}

void GameScene::setFormations(const FormationInfo *left, const FormationInfo *right) {
	me = left;
	opponent = right;
	createArmies();
}

void GameScene::addOneTimeNode(ManagedGameNodeInfo info) {
	_gameLayer->addChild(info.node);
	_managedNodes.push_back(info);
}

void GameScene::updateManagedGameNodes(float ticks) {
	for (auto it = _managedNodes.begin(); it != _managedNodes.end();) {
		ManagedGameNodeInfo &info = *it;
		bool removedNode = false;
		switch (info.lifeTimeType) {
			case GameNodeLifeTimeType::play_to_end: {
				if (info.nodeType == GameNodeType::frame_animation) {
					FrameAnimation *animation = (FrameAnimation *) info.node;
					animation->update(ticks);
					if (animation->isFinished()) {
						info.node->removeFromParent();
						it = _managedNodes.erase(it);

						removedNode = true;
					}
				} else if (info.nodeType == GameNodeType::skeletal_animation) {
					CCSkeletalSprite *animation = (CCSkeletalSprite *) info.node;
					animation->update(ticks);
					if (animation->animationIsDone()) {
						info.node->removeFromParent();
						it = _managedNodes.erase(it);

						removedNode = true;
					}
				}
				break;
			}
			case GameNodeLifeTimeType::duration: {
				info.duration -= ticks;
				if (info.duration <= 0.0f) {
					info.node->removeFromParent();
					it = _managedNodes.erase(it);

					removedNode = true;
				}
				break;
			}
			default:
				break;
		}

		if(!removedNode) {
			++it;
		}
	}
}

void GameScene::addNode(CCNode *node) {
	_gameLayer->addChild(node);
}

void GameScene::addBullet(Bullet *bullet) {
	_bullets.push_back(bullet);
}

void GameScene::updateBullets(float ticks) {
	for (auto it = _bullets.begin(); it != _bullets.end();) {
		Bullet *bullet = *it;
		if (!bullet->update(ticks)) {
			it = _bullets.erase(it);
			delete bullet;
		} else {
			++it;
		}
	}
}

void GameScene::createDialogLayer() {
	CCLayerColor *maskLayer = CCLayerColor::create(ccc4(10, 10, 10, 200));
	addChild(maskLayer);

	if (gameState == GameState::defeat) {
		DefeatLayer *layer = DefeatLayer::create();
		layer->targetScene = previousScene;
		maskLayer->addChild(layer);
	} else if (gameState == GameState::win) {
		VictoryLayer *layer = VictoryLayer::create();
		layer->targetScene = previousScene;
		maskLayer->addChild(layer);
	} else if (gameState == GameState::draw) {
		DefeatLayer *layer = DefeatLayer::create();
		layer->targetScene = previousScene;
		maskLayer->addChild(layer);
	}
}

void GameScene::handleBattleStandby(float ticks) {
	if (myArmy->isFrontSquadStandby() && myArmy->getLineSoldierCount(myArmy->frontLine) > 0 &&
			opponentArmy->isFrontSquadStandby() && opponentArmy->getLineSoldierCount(opponentArmy->frontLine) > 0) {
		transformBattleState(BattleState::march);
		_DHLOG("battle state -> march");

		float x = getContactPosition();
		myArmy->march(x);
		opponentArmy->march(x);
	} else {
		myArmy->updateFormationState();
		opponentArmy->updateFormationState();
	}
}

void GameScene::handleBattleMarch(float ticks) {
	if (myArmy->isFrontSquadAllDied() || opponentArmy->isFrontSquadAllDied()) {
		transformBattleState(BattleState::standby);
		_DHLOG("battle state -> standby");
	} else {
		myArmy->updateFormationState();
		opponentArmy->updateFormationState();

		float distance = opponentArmy->frontLinePosition - myArmy->frontLinePosition;
		if (distance < MELEE_H_RANGE && !hadAssignedUnits) {
			int myFrontSquad = myArmy->frontLine / LINES_PER_SQUAD;
			int myThirdLine = (myFrontSquad + 1) * LINES_PER_SQUAD - 1;

			int opponentFrontSquad = opponentArmy->frontLine / LINES_PER_SQUAD;
			int opponentThirdLine = (opponentFrontSquad + 1) * LINES_PER_SQUAD - 1;

			assignFrontLineMeleeTarget();

			adjustFrontLinePositions(myArmy);
			adjustFrontLinePositions(opponentArmy);

			if (myThirdLine - myArmy->frontLine > 0) {
				adjustSecondLinePositions(myArmy);
			}
			if (opponentThirdLine - opponentArmy->frontLine > 0) {
				adjustSecondLinePositions(opponentArmy);
			}

			if (myThirdLine - myArmy->frontLine > 1) {
				adjustThirdLinePositions(myArmy);
			}
			if (opponentThirdLine - opponentArmy->frontLine > 1) {
				adjustThirdLinePositions(opponentArmy);
			}

			hadAssignedUnits = true;
		}

		if(distance <= MELEE_SPACE) {
			transformBattleState(BattleState::melee);
			_DHLOG("battle state -> melee");
		} else {
			if (myArmy->isFrontSquadStandby() && opponentArmy->isFrontSquadStandby()) {
				transformBattleState(BattleState::standby);
				_DHLOG("battle state -> standby");
			}
		}
	}
}

void GameScene::visit() {
	CCNode::visit();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	ccDrawLine(ccp(myArmy->frontLinePosition, 0.0f), ccp(myArmy->frontLinePosition, 500.0f));
	ccDrawLine(ccp(opponentArmy->frontLinePosition, 0.0f), ccp(opponentArmy->frontLinePosition, 500.0f));

	for(int i = 0; i < ROWS_PER_LINE; ++i) {
		if(myArmy->positionTable[myArmy->frontLine][i] > 0) {
			ccDrawLine(ccp(myArmy->frontLinePosition - 10.0f, FORMATION_MARGIN + i * GRID_HEIGHT), ccp(myArmy->frontLinePosition + 10.0f, FORMATION_MARGIN + i * GRID_HEIGHT));
		}

		if(opponentArmy->positionTable[opponentArmy->frontLine][i] > 0) {
			ccDrawLine(ccp(opponentArmy->frontLinePosition - 10.0f, FORMATION_MARGIN + i * GRID_HEIGHT), ccp(opponentArmy->frontLinePosition + 10.0f, FORMATION_MARGIN + i * GRID_HEIGHT));
		}
	}

	int squadFrontLine = myArmy->frontLine / LINES_PER_SQUAD * LINES_PER_SQUAD;
	for(int i = myArmy->frontLine; i < squadFrontLine + LINES_PER_SQUAD; ++i) {
		for(int j = 0; j < ROWS_PER_LINE; ++j) {
			Unit* unit = myArmy->soldiers[i][j];
			if(unit && unit->target) {
				CCPoint pos = unit->sprite->getBonePositionRelativeToWorld("skeleton_head");
				CCPoint targetPos = unit->target->position;
				ccDrawLine(pos, targetPos);
			}
		}
	}

	squadFrontLine = opponentArmy->frontLine / LINES_PER_SQUAD * LINES_PER_SQUAD;
	for(int i = opponentArmy->frontLine; i < squadFrontLine + LINES_PER_SQUAD; ++i) {
		for(int j = 0; j < ROWS_PER_LINE; ++j) {
			Unit* unit = opponentArmy->soldiers[i][j];
			if(unit && unit->target) {
				CCPoint pos = unit->sprite->getBonePositionRelativeToWorld("skeleton_head");
				CCPoint targetPos = unit->target->position;
				ccDrawLine(pos, targetPos);
			}
		}
	}
#endif
}

void GameScene::handleBattleMelee(float ticks) {
	if (_endlessBattleTicks > ENDLESS_BATTLE_TIME) {
		gameState = GameState::draw;
	} else {
		if (_deaths > 0) {
			_endlessBattleTicks = 0;

			if (myArmy->isFrontSquadAllDied() || opponentArmy->isFrontSquadAllDied()) {
				transformBattleState(BattleState::standby);
				_DHLOG("battle state -> standby");
			} else {

			}
		} else {
			_endlessBattleTicks += ticks;
		}

		myArmy->updateFormationState();
		opponentArmy->updateFormationState();

		float distance = opponentArmy->frontLinePosition - myArmy->frontLinePosition;
		if (distance > MELEE_SPACE) {
			transformBattleState(BattleState::standby);
			_DHLOG("battle state -> standby");
		} else if (battleState == BattleState::melee && (_deaths > 0 || _arrivals > 0 || _idles > 0) ) {
			int myFrontSquad = myArmy->frontLine / LINES_PER_SQUAD;
			int myThirdLine = (myFrontSquad + 1) * LINES_PER_SQUAD - 1;

			int opponentFrontSquad = opponentArmy->frontLine / LINES_PER_SQUAD;
			int opponentThirdLine = (opponentFrontSquad + 1) * LINES_PER_SQUAD - 1;

			assignFrontLineMeleeTarget();

			adjustFrontLinePositions(myArmy);
			adjustFrontLinePositions(opponentArmy);

			if (myThirdLine - myArmy->frontLine > 0) {
				adjustSecondLinePositions(myArmy);
			}
			if (opponentThirdLine - opponentArmy->frontLine > 0) {
				adjustSecondLinePositions(opponentArmy);
			}

			if (myThirdLine - myArmy->frontLine > 1) {
				adjustThirdLinePositions(myArmy);
			}
			if (opponentThirdLine - opponentArmy->frontLine > 1) {
				adjustThirdLinePositions(opponentArmy);
			}
		}
	}
}

bool GameScene::isContact() {
	float x = getContactPosition();
	float left = myArmy->frontLinePosition;
	float right = opponentArmy->frontLinePosition;

	return false;
}

void GameScene::screenshot(CCObject *sender) {
	CCRenderTexture *tex = CCRenderTexture::create(static_cast<int>(GM->screenSize.width), static_cast<int>(GM->screenSize.height));
	tex->begin();
	this->visit();
	tex->end();

	tm* t = localtime(0);
	sprintf(buf, "bc_screenshot_%02d%02d_%02d%02d%02d.png", t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
	tex->saveToFile(buf, kCCImageFormatPNG);
}

void GameScene::assignFrontLineMeleeTarget() {
	bool assignedTable[ROWS_PER_LINE];
	memset(assignedTable, 0, ROWS_PER_LINE);

	for (int i = 0; i < ROWS_PER_LINE; ++i) {
		Unit *unit = myArmy->getSoldierForAssign(myArmy->frontLine, i);
		if (!unit) {
			assignedTable[i] = true;
			continue;
		}
	}

	for (int step = 0; step < ROWS_PER_LINE; ++step) {
		for (int i = 0; i < ROWS_PER_LINE; ++i) {
			if (assignedTable[i]) {
				continue;
			}

			Unit *unit = myArmy->getSoldier(myArmy->frontLine, i);
			if(unit->target) {
				assignedTable[i] = true;
				continue;
			}

			if (step == 0) {
				Unit *target = opponentArmy->getSoldierForAssign(opponentArmy->frontLine, i);
				if (target) {
					target->target = unit;
					unit->target = target;

					assignedTable[i] = true;

					unit->startAttack();
					target->startAttack();
				}
			} else {
				int myDeltaRows = step / 2;
				int opponentDeltaRows = step / 2;

				if (step % 2 > 0) {
					myDeltaRows++;
				}

				bool isAssigned = false;
				if (i + step < ROWS_PER_LINE) {
					Unit *upTarget = opponentArmy->getSoldierForAssign(opponentArmy->frontLine, i + step);
					if (upTarget) {
						bool haveUpBlocks = false;
						for (int m = i + 1; m <= (i + step); ++m) {
							haveUpBlocks = !myArmy->testGridForAssigningUnit(unit, myArmy->frontLine, m);
							if(haveUpBlocks) {
								break;
							}
						}

						if(!haveUpBlocks) {
							for (int m = i + step - 1; m >= i; --m) {
								haveUpBlocks = !opponentArmy->testGridForAssigningUnit(upTarget, opponentArmy->frontLine, m);
								if(haveUpBlocks) {
									break;
								}
							}
						}

						if (!haveUpBlocks) {
							upTarget->target = unit;
							unit->target = upTarget;

							if (myDeltaRows > 0) {
								unit->assignTo(ccp(unit->targetPosition.x, FORMATION_MARGIN + (i + myDeltaRows) * GRID_HEIGHT), myArmy->frontLine, i + myDeltaRows);
							}
							if (opponentDeltaRows > 0) {
								upTarget->assignTo(ccp(upTarget->targetPosition.x, FORMATION_MARGIN + (i + step - opponentDeltaRows) * GRID_HEIGHT), opponentArmy->frontLine, i + step - opponentDeltaRows);
							}

							unit->startAttack();
							upTarget->startAttack();

							isAssigned = true;

							assignedTable[i] = true;
						}
					}
				}

				if (!isAssigned && i - step >= 0) {
					Unit *downTarget = opponentArmy->getSoldierForAssign(opponentArmy->frontLine, i - step);
					if (downTarget) {
						bool haveDownBlocks = false;
						for (int m = i - 1; m >= (i - step); --m) {
							haveDownBlocks = !myArmy->testGridForAssigningUnit(unit, myArmy->frontLine, m);
							if(haveDownBlocks) {
								break;
							}
						}

						if(!haveDownBlocks) {
							for (int m = i - step + 1; m <= i; ++m) {
								haveDownBlocks = !opponentArmy->testGridForAssigningUnit(downTarget, opponentArmy->frontLine, m);
								if(haveDownBlocks) {
									break;
								}
							}
						}

						if (!haveDownBlocks) {
							downTarget->target = unit;
							unit->target = downTarget;

							assignedTable[i] = true;

							if (myDeltaRows > 0) {
								unit->assignTo(ccp(unit->targetPosition.x, FORMATION_MARGIN + (i - myDeltaRows) * GRID_HEIGHT), myArmy->frontLine, i - myDeltaRows);
							}
							if (opponentDeltaRows > 0) {
								downTarget->assignTo(ccp(downTarget->targetPosition.x, FORMATION_MARGIN + (i - step + opponentDeltaRows) * GRID_HEIGHT), opponentArmy->frontLine, i - step + opponentDeltaRows);
							}

							unit->startAttack();
							downTarget->startAttack();
						}
					}
				}
			}
		}
	}
}

void GameScene::adjustFrontLinePositions(Army *army) {
	bool assignedTable[ROWS_PER_LINE];
	memset(assignedTable, 0, ROWS_PER_LINE);

	for (int i = 0; i < ROWS_PER_LINE; ++i) {
		Unit *unit = army->getSoldier(army->frontLine, i);
		if (!unit || !unit->isAlive() || unit->target || unit->isAssigning) {
			assignedTable[i] = true;
		}
	}

	for (int step = 1; step < ROWS_PER_LINE; ++step) {
		for (int i = 0; i < ROWS_PER_LINE; ++i) {
			if (assignedTable[i]) {
				continue;
			}

			Unit *unit = army->getSoldier(army->frontLine, i);
			int targetRowIndex = -1;

			int upRowIndex = i + step;
			if (upRowIndex < ROWS_PER_LINE) {
				Unit *target = unit->theirArmy->getSoldier(unit->theirArmy->frontLine, upRowIndex);
				if (target && target->isAlive()) {
					targetRowIndex = upRowIndex;
				}
			}

			int downRowIndex = i - step;
			if (targetRowIndex < 0 && downRowIndex >= 0) {
				Unit *target = unit->theirArmy->getSoldier(unit->theirArmy->frontLine, downRowIndex);
				if (target && target->isAlive()) {
					targetRowIndex = downRowIndex;
				}
			}

			if (targetRowIndex >= 0) {
				if (targetRowIndex > i) {
					for (int j = i + 1; j <= targetRowIndex; ++j) {
						if (!army->testGridForAssigningUnit(unit, army->frontLine, j)) {
							CCPoint targetPos = ccp(unit->targetPosition.x, FORMATION_MARGIN + (j - 1) * GRID_HEIGHT);
							unit->assignTo(targetPos, army->frontLine, j - 1);

							break;
						}
					}
				} else if (targetRowIndex < i) {
					for (int j = i - 1; j >= targetRowIndex; --j) {
						if (!army->testGridForAssigningUnit(unit, army->frontLine, j)) {
							CCPoint targetPos = ccp(unit->targetPosition.x, FORMATION_MARGIN + (j + 1) * GRID_HEIGHT);
							unit->assignTo(targetPos, army->frontLine, j + 1);

							break;
						}
					}
				}

				assignedTable[i] = true;
			}
		}
	}
}

void GameScene::adjustSecondLinePositions(Army *army) {
	bool assignedTable[ROWS_PER_LINE];
	memset(assignedTable, 0, ROWS_PER_LINE);

	float fontLineTargetPosition = army->getFrontLineTargetPosition();

	for (int i = 0; i < ROWS_PER_LINE; ++i) {
		if (army->positionTable[army->frontLine][i] > 0) {
			assignedTable[i] = true;
		}
	}

	for (int step = 0; step < ROWS_PER_LINE; ++step) {
		for (int i = 0; i < ROWS_PER_LINE; ++i) {
			if (assignedTable[i]) {
				continue;
			}

			if (step == 0) {
				Unit *unit = army->getSoldierForAssign(army->frontLine + 1, i);
				if (unit) {
					unit->assignTo(ccp(fontLineTargetPosition, unit->position.y), army->frontLine, i);
					assignedTable[i] = true;
				}
			} else {
				bool isAssigned = false;

				if ((i + step) < ROWS_PER_LINE) {
					Unit *upUnit = army->getSoldierForAssign(army->frontLine + 1, i + step);
					if (upUnit) {
						bool haveUpBlocks = false;
						for (int m = i + 1; m < (i + step); ++m) {
							haveUpBlocks = !myArmy->testGridForAssigningUnit(upUnit, myArmy->frontLine + 1, m);
							if(haveUpBlocks) {
								break;
							}
						}

						if (!haveUpBlocks) {
							upUnit->assignTo(ccp(fontLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine, i);
							assignedTable[i] = true;

							isAssigned = true;
						}
					}
				}

				if (!isAssigned && (i - step >= 0)) {
					Unit *downUnit = army->getSoldierForAssign(army->frontLine + 1, i - step);
					if (downUnit) {
						bool haveDownBlocks = false;
						for (int m = i - 1; m > (i - step); --m) {
							haveDownBlocks = !myArmy->testGridForAssigningUnit(downUnit, myArmy->frontLine + 1, m);
							if(haveDownBlocks) {
								break;
							}
						}

						if (!haveDownBlocks) {
							downUnit->assignTo(ccp(fontLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine, i);
							assignedTable[i] = true;
						}
					}
				}
			}
		}
	}
}

void GameScene::adjustThirdLinePositions(Army *army) {
	bool assignedTable[ROWS_PER_LINE];
	memset(assignedTable, 0, ROWS_PER_LINE);

	float fontLineTargetPosition = army->getFrontLineTargetPosition();
	float secondLineTargetPosition;
	if (army == myArmy) {
		secondLineTargetPosition = army->getFrontLineTargetPosition() - GRID_WIDTH;
	} else {
		secondLineTargetPosition = army->getFrontLineTargetPosition() + GRID_WIDTH;
	}

	for (int i = 0; i < ROWS_PER_LINE; ++i) {
		if (army->positionTable[army->frontLine][i] > 0) {
			assignedTable[i] = true;
			continue;
		}
	}

	// cover front line
	for (int step = 0; step < ROWS_PER_LINE; ++step) {
		for (int i = 0; i < ROWS_PER_LINE; ++i) {
			if (assignedTable[i]) {
				continue;
			}

			if (step == 0) {
				Unit *unit = army->getSoldierForAssign(army->frontLine + 2, i);
				if (unit) {
					unit->assignTo(ccp(fontLineTargetPosition, unit->position.y), army->frontLine, i);
					assignedTable[i] = true;
				}
			} else {

				if ((i + step) < ROWS_PER_LINE) {
					Unit *upUnit = army->getSoldierForAssign(army->frontLine + 2, i + step);
					if (upUnit) {
						bool haveUpBlocks = false;
						for (int m = i + 1; m < (i + step); ++m) {
							haveUpBlocks = !myArmy->testGridForAssigningUnit(upUnit, myArmy->frontLine + 2, m);
							if(haveUpBlocks) {
								break;
							}
						}

						if (upUnit && !haveUpBlocks) {
							upUnit->assignTo(ccp(fontLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine, i);
							assignedTable[i] = true;
						}
					}
				}

				if ((i - step) >= 0) {
					Unit *downUnit = army->getSoldierForAssign(army->frontLine + 2, i - step);
					if (downUnit) {
						bool haveDownBlocks = false;
						for (int m = i - 1; m > (i - step); --m) {
							haveDownBlocks = !myArmy->testGridForAssigningUnit(downUnit, myArmy->frontLine + 2, m);
							if(haveDownBlocks) {
								break;
							}
						}

						if (!haveDownBlocks) {
							downUnit->assignTo(ccp(fontLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine, i);
							assignedTable[i] = true;
						}
					}
				}
			}
		}
	}

	memset(assignedTable, 0, ROWS_PER_LINE);
	for (int i = 0; i < ROWS_PER_LINE; ++i) {
		if (army->positionTable[army->frontLine + 1][i] > 0) {
			assignedTable[i] = true;
			continue;
		}
	}

	// cover second line
	for (int step = 0; step < ROWS_PER_LINE; ++step) {
		for (int i = 0; i < ROWS_PER_LINE; ++i) {
			if (assignedTable[i]) {
				continue;
			}

			if (step == 0) {
				Unit *unit = army->getSoldierForAssign(army->frontLine + 2, i);
				if (unit) {
					unit->assignTo(ccp(secondLineTargetPosition, unit->position.y), army->frontLine + 1, i);
					assignedTable[i] = true;
				}
			} else {
				if ((i + step) < ROWS_PER_LINE) {
					Unit *upUnit = army->getSoldierForAssign(army->frontLine + 2, i + step);
					if (upUnit) {
						bool haveUpBlocks = false;
						for (int m = i + 1; m < (i + step); ++m) {
							haveUpBlocks = !myArmy->testGridForAssigningUnit(upUnit, myArmy->frontLine + 2, m);
							if(haveUpBlocks) {
								break;
							}
						}
						if (!haveUpBlocks) {
							upUnit->assignTo(ccp(secondLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine + 1, i);
							assignedTable[i] = true;
						}
					}
				}


				if ((i - step) >= 0) {
					Unit *downUnit = army->getSoldierForAssign(army->frontLine + 2, i - step);
					if (downUnit) {
						bool haveDownBlocks = false;
						for (int m = i - 1; m > (i - step); --m) {
							haveDownBlocks = !myArmy->testGridForAssigningUnit(downUnit, myArmy->frontLine + 2, m);
							if(haveDownBlocks) {
								break;
							}
						}
						if (!haveDownBlocks) {
							downUnit->assignTo(ccp(secondLineTargetPosition, FORMATION_MARGIN + i * GRID_HEIGHT), army->frontLine + 1, i);
							assignedTable[i] = true;
						}
					}
				}
			}
		}
	}
}

void GameScene::transformBattleState(BattleState newState) {
	switch(newState) {
		case BattleState::standby:
			break;
		case BattleState::march:
			hadAssignedUnits = false;
			break;
		case BattleState::melee:
			break;
		default:break;
	}

	battleState = newState;
}

